import {
  CollationSpec,
  Options,
  PipelineOperator,
  ProcessingMode
} from "../core";
import { AsyncIterator, AsyncLazy, asyncConcat } from "./lazy";
import { $limit } from "../operators/pipeline/limit";
import { $project } from "../operators/pipeline/project";
import { $skip } from "../operators/pipeline/skip";
import { $sort } from "../operators/pipeline/sort";
import { Any, AnyObject, Predicate } from "../types";
import { cloneDeep, has } from "../util";
import { initOptions } from "../core";


// 异步游标接口，支持异步迭代
export interface AsyncCursor<T> extends AsyncIterable<T> {
  // 异步获取所有结果
  all(): Promise<T[]>;

  // 异步计数
  count(): Promise<number>;

  // 链式操作方法
  skip(n: number): AsyncCursor<T>;
  limit(n: number): AsyncCursor<T>;
  sort(modifier: AnyObject): AsyncCursor<T>;
  collation(spec: CollationSpec): AsyncCursor<T>;

  // 其他方法
  map<R>(f: (value: T, index: number, array: T[]) => R | Promise<R>): Promise<R[]>;
  forEach(f: (value: T, index: number, array: T[]) => void | Promise<void>): Promise<void>;
}
const OPERATORS: Record<string, PipelineOperator> = { $sort, $skip, $limit };

type ArrayCallback<R, T> = (value: T, index: number, array: T[]) => R | Promise<R>;

/**
 * The `AsyncCursor` class provides a mechanism for asynchronously iterating over a collection of data
 * with support for filtering, projection, sorting, skipping, and limiting results.
 * It is designed to be chainable and supports lazy evaluation for efficient data processing.
 *
 * @template T - The type of the elements in the cursor.
 */
export class AsyncCursorImpl<T> implements AsyncCursor<T> {
  #source: AsyncIterator;
  #predicate: Predicate<Any>;
  #projection: AnyObject;
  #options: Options;
  #operators: AnyObject = {};
  #result: AsyncIterator | null = null;
  #buffer: T[] = [];

  /**
   * Creates an instance of the AsyncCursor class.
   *
   * @param source - The source of data to be iterated over.
   * @param predicate - A function or condition to filter the data.
   * @param projection - An object specifying the fields to include or exclude in the result.
   * @param options - Optional settings to customize the behavior of the cursor.
   */
  constructor(
    source: any,
    predicate: Predicate<Any>,
    projection: AnyObject,
    options?: Options
  ) {
    this.#source = AsyncLazy(source);
    this.#predicate = predicate;
    this.#projection = projection;
    // 确保即使没有提供选项，也有默认选项
    this.#options = options ? initOptions(options) : initOptions({});
  }

  /** Returns the async iterator from running the query */
  private async fetch(): Promise<AsyncIterator> {
    if (this.#result) return this.#result;

    // apply filter
    this.#result = this.#source.filter(this.#predicate);
    const mode = this.#options.processingMode;

    // handle processing flag.
    if (mode & ProcessingMode.CLONE_INPUT) this.#result.map(cloneDeep);

    // apply cursor operators
    for (const op of ["$sort", "$skip", "$limit"]) {
      if (has(this.#operators, op)) {
        // Note: For now, we're using the sync operators with async data
        // In a full implementation, these would need to be async versions
       this.#result = (await OPERATORS[op](
          this.#result,
          this.#operators[op],
          this.#options
        )) as AsyncIterator;
      }
    }

    if (Object.keys(this.#projection).length) {
          this.#result = await $project(this.#result, this.#projection, this.#options) as AsyncIterator;
        }

    if (mode & ProcessingMode.CLONE_OUTPUT) this.#result.map(cloneDeep);

    return this.#result;
  }

  async *[Symbol.asyncIterator](): AsyncIterableIterator<T> {
    const result = await this.fetch();
    yield* result as unknown as AsyncIterable<T>;
  }

  /** Returns all result of the query as an array */
  async all(): Promise<T[]> {
    return await this.fetchAll();
  }

  /** Returns the count of results */
  async count(): Promise<number> {
    const result = await this.fetch();
    return await result.size();
  }

  private async fetchAll(): Promise<T[]> {
    const result = await this.fetch();
    return await result.value();
  }

  map<R>(f: ArrayCallback<R, T>): Promise<R[]> {
    return this.all().then(arr => Promise.all(arr.map(f)));
  }

  async forEach(f: ArrayCallback<void, T>): Promise<void> {
    const arr = await this.all();
    for (let i = 0; i < arr.length; i++) {
      await f(arr[i], i, arr);
    }
  }

  skip(n: number): AsyncCursor<T> {
    this.#operators["$skip"] = n;
    return this;
  }

  limit(n: number): AsyncCursor<T> {
    this.#operators["$limit"] = n;
    return this;
  }

  sort(modifier: AnyObject): AsyncCursor<T> {
    this.#operators["$sort"] = modifier;
    return this;
  }

  collation(spec: CollationSpec): AsyncCursor<T> {
    this.#operators["$collation"] = spec;
    return this;
  }

  async value(): Promise<T[]> {
    return this.all();
  }
}